import { FlightRecords, FlightItem } from '../compute/flightRecords';
import { FlightLaunchesView } from  '../view/flight_launches/launchesView';

function createPendant(context) {
  let t0 = new P(context);
  global.t0 = t0;

  let pendant = new context.Pendant({
    require : ['zrender'],
    init : (libs, args) => t0.init(libs, args),
    destroy : () => t0.destroy(),

    // custom function
    collectInfo : () => t0.collectInfo(),
    onMessage : (key, value) => t0.onMessage(key, value),
  });
  t0.sendStatus = () => pendant.sendStatus();
  return pendant;
}

const supportParams = [
  { method: 'latest', supports: ['offset', 'count'] },
  { method: 'year', supports: ['offset', 'count', 'year'] },
  { method: 'conditions', supports: ['offset', 'count', 'vehicles', 'orbits', 'sites', 'important', 'tags', 'payload'] },
];
const supportMatchTypes = {
  'contain' : (/** @type {string} */ text, /** @type {string} */ param) => text.includes(param),
  'not' : (/** @type {string} */ text, /** @type {string} */ param) => !text.includes(param),
  'prefix' : (/** @type {string} */ text, /** @type {string} */ param) => text.startsWith(param),
  'suffix' : (/** @type {string} */ text, /** @type {string} */ param) => text.endsWith(param),
  'regex' : (/** @type {string} */ text, /** @type {string} */ param) => text.match(param) != null,
};

class P {
  constructor(context) {
    this.context = context;
    this.records = new FlightRecords({ assetPath: context.assetPath });
  }

  init(libs, args) {
    this.view = new FlightLaunchesView({
      context: this.context,
      dom: args.dom,
      zrender: libs.zrender,
      storage: this.records,
    });

    this.view.on('view/resize', (_, value) => {
      this.arg.view.width = value.width;
      this.arg.view.height = value.height;
      this.sendStatus();
    });
    this.view.startTick();

    this.asyncLoadRecords()
        .then(list => this.view.setRecords(list));
  }

  asyncLoadRecords() {
    if (this.arg.params == null) {
      this.arg.params = {};
    }

    switch(this.arg.method) {
      case "year": {
        const { year, offset, count } = this.arg.params;
        return this.records.load(year)
          .then(list => {
            let startIdx = list.length < (offset + count) ? 0 : list.length - (offset + count);
            let endIdx = list.length < offset ? 0 : list.length - offset;
            return list.slice(startIdx, endIdx);
          });
      }
      case "conditions":
        return this.filterRecordsByConditions();
      case "latest": default: {
        const { offset, count } = this.arg.params;
        return this.records.getLatestRecords(offset, count);
      }
    }
  }

  asyncLoadCards() {
    this.arg.cards = this.view.setCards(this.arg.cards);
  }

  filterRecordsByConditions() {
    /** @type {((item:FlightItem)=>boolean)[]} */
    const conditions = [];
    const { vehicles, orbits, sites, important, tags, flags, payload, payloadMatchType } = this.arg.params;
    console.log('this.arg.params => ', this.arg.params)

    if (vehicles) {
      let set = {};
      vehicles.forEach(vText => set[vText] = true);
      conditions.push((item) => item.vehicles.some(v => set[v.name] === true));
    }
    if (orbits) {
      let set = {};
      orbits.forEach(v => set[v] = true);
      conditions.push((item) => set[item.orbit] === true)
    }
    if (sites) {
      let set = {};
      sites.forEach(v => set[v] = true);
      conditions.push((item) => set[item.site] === true)
    }
    if (important) {
      conditions.push((item) => item.isImportant())
    }
    if (tags) {
      let set = {};
      tags.forEach(v => set[v] = true);
      conditions.push((item) => item.tags.some(v => set[v] === true));
    }
    if (flags) {
      conditions.push((item) => item.flags & flags === flags);
    }
    if (payload && payloadMatchType) {
      /** @type {(text: string, param: string) => boolean} */
      let func = supportMatchTypes[payloadMatchType];
      conditions.push((item) => item.payloads.some(v => func(v, payload)));
    }

    const { offset, count } = this.arg.params;

    return this.records.getLatestRecords(offset, count, (item) => {
      return conditions == null || conditions.every(filter => filter(item));
    });
  }

  arg = {
    format : "flightLog/cn/ver2023",
    method : "latest",
    params : {
      offset : 0,
      count : 50,
    },
    view : {
      width : 400,
      height : 400,
    },
    /**
     * @type {{type:string,params:object}[]} 
     * 因为历史原因, 'flights' 的 params 放在 arg.params 里面, 其它的 cards 的 param 放这里
     */
    cards : [
      {
        type : "flights",
        params : {}
      }
    ]
  };

  collectInfo() {
    return this.arg;
  }

  // onMessage('update', {method:'latest', params:{offset: 0, count: 50}})
  // onMessage('update', {method:'year', params:{year: 2022, offset: 0, count: 50}})
  // onMessage('update', {method:'conditions', params:{tags:['CLEP', '神舟']}})
  onMessage(key, value) {
    switch (key) {
			case 'update': {
        let success = this.updateFlightParams(value.method, value.params || {});
        if (success) {
          this.asyncLoadRecords()
            .then(list => this.view.setRecords(list));
        } else {
          console.error('update 失败', value.method, value.params);
        }
				this.sendStatus();
      } return;

      // onMessage('layout', {cards:[]})
      case 'layout': {
        let success = this.updateCards(value.cards);
        if (success) {
          this.asyncLoadCards();
        }
      } return;

      // onMessage('style', {bg: 'transparent'})
      case 'style': {
        if (typeof(value.bg) == 'string') {
          this.view.setBgColor(value.bg);
        }
				this.sendStatus();
      } return;
		}
		console.log("flight_launches 收到命令", key, value);
  }

  /**
   * @param {string} method
   * @param {object} params
   * @returns 修改是否成功
   */
  updateFlightParams(method, params) {
    const supports = supportParams.filter(p => p.method === method)[0];
    if (!supports) {
      return false;
    }

    const tmp = {};
    supports.supports.forEach(p => {
      switch (p) {
        case 'offset': {
          tmp[p] = params[p] == null ? 0 : parseInt(params[p]);
          if (tmp[p] < 0) {
            return false;
          }
        } break;
        case 'count': {
          tmp[p] = params[p] == null ? 50 : parseInt(params[p]);
          if (tmp[p] < 0) {
            return false;
          }
        } break;
        case 'year': {
          tmp.year = params.year == null ? new Date().getFullYear() : parseInt(params.year);
          if (tmp.year < 2013) {
            return false;
          }
        } break;
        case 'vehicles': case 'orbits': case 'sites': case 'tags': {
          const array = params[p];
          if (!(array instanceof Array)) {
            return false;
          }
          if (!array.every(v => typeof(v) == 'string')) {
            return false;
          }
          tmp[p] = array;
        } break;
        case 'important': {
          if (params.important) {
            tmp.important = true;
          }
        } break;
        case 'payload': {
          let payloadKeyword = params.payload;
          if (supportMatchTypes[params.payloadMatchType] == null) {
            return false;
          }
          if (typeof(payloadKeyword) !== 'string' || payloadKeyword.length === 0) {
            return false;
          }

          tmp.payloadMatchType = params.payloadMatchType;
          tmp.payload = payloadKeyword;
        } break;
        default:
          console.error('flight_launches.updateFlightParams 不支持 param =', p);
          return false;
      }
    });

    this.arg.method = method;
    this.arg.params = tmp;
    return true;
  }

  /**
   * @param {({type:string,params:{}}|string)[]} cards 
   * @returns {boolean} 修改是否成功
   */
  updateCards(cards) {
    if (!(cards instanceof Array)) {
      return false;
    }

    /** @type {{type:string,params:{}}[]} */
    let params = [];
    for (let i = 0; i < cards.length; i++) {
      let card = cards[i];
      if (typeof(card) == 'string') {
        params.push({ type: card, params: {} });
        continue;
      }
      if (typeof(card) != 'object' || typeof(card.type) != 'string' || typeof(card.params) != 'object') {
        return false;
      }
      params.push({ type: card.type, params: card.params });
    }

    let flightParams = params.filter(p => p.type == 'flights');
    if (flightParams.length != 1) {
      console.error('输入参数有误: layouts 中需要有且只有一个名为 flights 的 card');
      return false;
    }

    this.arg.cards = params;
    return true;
  }

  destroy() {
    this.view.destroy();
  }
}

export default createPendant;
